//--------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/boot.h>
#include <string.h>
#include <ctype.h>

#include "dkbasic.h"
#include "dkstring.h"
#include "dkexpression.h"
#include "dkfunctions.h"
#include "dkprogram.h"
#include "dkterm.h"
#include "dkfile_uart.h"
#include "dkvariables.h"

//--------------------------------------------------------------------
// note: function handlers added here must also be added to the list in dkfunctiontable.S
//--------------------------------------------------------------------


/*
ASC Function
Purpose:
To return a numeric value that is the ASCII code for the first character of the string x$.
Syntax:
ASC(x$)
*/
dkexpression *dkfunction_asc( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	dkexpression *param;				// param will point to the result of expression passed to us
	char t[ MAX_TOKEN_LENGTH+1 ];


	if( !nameflags&DKNAME_PAREN )		// check that we found opening parenthesis (
		return NULL;					// error: syntax error

	line = dkgettoken( line, t );		// get token - (

	param = expression( line );			// process expression @line
	if( !param )	
		return NULL;					// error: missing operand

	line = expstr;						// update line pointer to point to where expression() left off
	

	expstr = dkgettoken( line, t );		// get token - )
	if( t[0] != ')' )
	{
		dkexpfree(param);
		return NULL;					// error: syntax error (missing closing parenthesis)
	}

	if( param->type != DKVAR_STRING )
	{
		dkexpfree(param);
		return NULL;					// error: type mismatch
	}
	if( !strlen( ((char **)param->data)[0] ) )
	{
		dkexpfree(param);
		return NULL;					// error: illegal function call
	}


	e = dknewexp();
	if( !e )
		return NULL;					// memory allocation error

	e->type = DKVAR_INTEGER;
	e->data = (int16_t *)malloc( sizeof( int16_t ) );
	if( !e->data )
	{
		dkexpfree(e);
		dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}
		
	((int16_t *)e->data)[0] = ((char **)param->data)[0][0];	// return integer value of first byte in string


	dkexpfree(param);


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_hex( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	char t[MAX_TOKEN_LENGTH+1];
	uint16_t hexvalue;
	int hexdigits=0;
	int i;


	if( !(nameflags & DKNAME_STRING) )
	{
		dkstatus = DKSTATUS_TYPEMISMATCH;
		return NULL;
	}

	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand

	if( e->type == DKVAR_STRING )
	{
		hexvalue = ((char **)e->data)[0][0];		// if hex$() is passed a string, use first byte of string
	}
	else if( e->type == DKVAR_BYTE )
	{
		hexvalue = ((uint8_t *)e->data)[0];
	}
	else
	{
		dkexptoint( e );
		if( e->type != DKVAR_INTEGER )
		{
			dkexpfree( e );
			if( !dkstatus )
				dkstatus = DKSTATUS_TYPEMISMATCH;
			return NULL;
		}
		hexvalue = ((uint16_t *)e->data)[0];
	}
	
	dkexpfree(e);
	if( (e=dkgetnextexpression( line )) )			// get next expression (optional) # of hex digits to print
	{
		line = expstr;
		dkexptoint(e);
		if( e->type != DKVAR_INTEGER )
		{
			dkexpfree( e );
			if( !dkstatus )
				dkstatus = DKSTATUS_TYPEMISMATCH;
			return NULL;
		}
		hexdigits = ((int16_t *)e->data)[0];
	}

	dkexpfree(e);
	if( (e=dkgetnextexpression( line )) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}

	e = dknewexp();
	if( !e )
		return NULL;					// malloc() error


	snprintf( t, MAX_TOKEN_LENGTH, "%X", hexvalue );
	e->data = (char **)malloc( sizeof( char * ) );
	
	if( hexdigits )
	{
		((char **)e->data)[0] = (char *)malloc( hexdigits+1 );
		if( strlen(t) <= hexdigits )
		{
			for( i=0; i<hexdigits; i++ )
				((char **)e->data)[0][i] = '0';
			strcpy( ((char **)e->data)[0]+hexdigits-strlen(t), t );
		}
		else
		{
			strncpy( ((char **)e->data)[0], t, hexdigits );
			((char **)e->data)[0][hexdigits] = '\0';
		}
	}
	else
	{
		((char **)e->data)[0] = (char *)malloc( strlen(t)+1 );
		strcpy( ((char **)e->data)[0], t );
	}

	e->type = DKVAR_STRING;


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_chr( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	char c;


	if( !(nameflags & DKNAME_STRING) )
	{
		dkstatus = DKSTATUS_TYPEMISMATCH;
		return NULL;
	}

	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}

	dkexptoint( e );

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}
	c = ((uint16_t *)e->data)[0];
	
	dkexpfree( e );
	e = dknewexp();
	
	e->type = DKVAR_STRING;
	e->data = (char **)malloc( sizeof( char * ) );
	((char **)e->data)[0] = (char *)malloc( 2 );
	if( !((char **)e->data)[0] )
	{
		dkexpfree(e);
		dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}

	((char **)e->data)[0][0] = c;
	((char **)e->data)[0][1] = '\0';


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_peek( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	int8_t value;


	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}
	
	dkexptoint( e );

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}

	value = *(uint8_t *)( ((int16_t *)e->data)[0] );	// return value @ address pointed to by expression
	
	dkexptobyte( e );
	((int8_t *)e->data)[0] = value;


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_bit( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return


	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}
	
	dkexptoint( e );

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}

	((int16_t *)e->data)[0] = (1<<((int16_t *)e->data)[0]);	// return value of this bit


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_test( char *line, uint16_t nameflags )
{
	dkexpression *e;

	e = dknewexp();
	if(!e)
		return NULL;

	e->type = DKVAR_INTEGER;
	((int16_t *)e->data)[0] = 42;

	return e;
}
//--------------------------------------------------------------------

dkexpression *dkfunction_inkey( char *line, uint16_t nameflags )
{
	dkexpression *e;

	
	if( nameflags != DKNAME_STRING )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_TYPEMISMATCH;
		return NULL;					// nameflags should be DKNAME_STRING or its a bad function call
	}


	e = dknewexp();
	if(!e)
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}

	e->type = DKVAR_STRING;
	e->data = (char **)malloc( sizeof( char * ) );
	((char **)e->data)[0] = (char *)malloc( 2 );
	if( !((char **)e->data)[0] )
	{
		dkexpfree(e);
		dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}

	((char **)e->data)[0][1] = '\0';						// second byte of string = NULL terminator

	if( _kbhit() ) 
		((char **)e->data)[0][0] = dkterm_get(NULL);			// return with one byte = received byte
	else
		((char **)e->data)[0][0] = '\0';					// return with empty string

//		((char *)e->value)[0] = '.';			// return with one byte = received byte


	return e;
}
//--------------------------------------------------------------------


dkexpression *dkfunction_csrlin( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return


	e = dknewexp();
	if( !e )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;					// memory allocation error
	}

	e->type = DKVAR_INTEGER;
	e->data = (int16_t *)malloc(sizeof(int16_t));
	((int16_t *)e->data)[0] = dkscreen.row;			// return current cursor row


	return e;
}

//--------------------------------------------------------------------

dkexpression *dkfunction_pos( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return


	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand (TODO: set dkstatus)
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}

	dkexpfree( e );	

	e = dknewexp();
	if( !e )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;					// memory allocation error
	}

	e->type = DKVAR_INTEGER;
	e->data = (int16_t *)malloc(sizeof(int16_t));
	((int16_t *)e->data)[0] = dkscreen.col;			// return current cursor column position


	return e;
}

//--------------------------------------------------------------------
dkexpression *dkfunction_eof( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	DKFILE *filehandle;
	

	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}
	
	dkexptoint( e );

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}


	filehandle = dkfilebynum( ((int16_t *)e->data)[0] );
	if( !filehandle )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_BADFILENUMBER;		// TODO: find why Syntax error happens instead of bad file number error?
//		return NULL;								// because of return NULL?
		return e;
	}
	
	((int16_t *)e->data)[0] = dkfeof( filehandle );


	return e;
}


//--------------------------------------------------------------------
// NOTE: because strings are NULL-terminated, if a 0 byte is received in input$(), there may be problems with following data?
dkexpression *dkfunction_input( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	uint16_t length=0;
	uint16_t filenum=0;
	char t[ MAX_TOKEN_LENGTH+1 ];
	char *lptr;
	DKFILE *filehandle;
	

	if( !(nameflags & DKNAME_STRING) )
	{
		dkstatus = DKSTATUS_TYPEMISMATCH;
		return NULL;
	}


	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	
	dkexptoint( e );
	length = ((int16_t *)e->data)[0];

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}

	dkexpfree( e );
	
	lptr = dkgettoken( line, t );			// step over #
	if( t[0] == ',' )
	{
		line = lptr;

		lptr = dkgettoken( line, t );			// step over #
		if( t[0] == '#' )
			line = lptr;
		
		e = expression( line );				// e = next expression (file #)
		line = expstr;
		if( !e )
			return NULL;					// error: missing operand

		dkexptoint( e );
		filenum = ((int16_t *)e->data)[0];

		if( e->type != DKVAR_INTEGER )
		{
			dkexpfree(e);
			return NULL;					// error: type mismatch
		}

		dkexpfree(e);

	}


	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		printf_P(PSTR("a"));
		return NULL;					// error: syntax error - should be no more parameters
	}



	e = dknewexp();
	if( !e )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;					// memory allocation error
	}

	e->type = DKVAR_STRING;
	e->data = (char **)malloc(sizeof(char *));
	((char **)e->data)[0] = (char *)malloc( length+1 );

	((char **)e->data)[0][length] = '\0';

	if( !filenum )
		fread( ((char **)e->data)[0], length, 1, stdin );
	else
	{
		filehandle = dkfilebynum( filenum );
		if( !filehandle )
		{	
			if( !dkstatus )
				dkstatus = DKSTATUS_BADFILENUMBER;		// TODO: find why Syntax error happens instead of bad file number error?
//			return NULL;								// because of return NULL?
			return e;
		}
		dkfread( ((char **)e->data)[0], length, 1, filehandle );
	}


	return e;
}


//--------------------------------------------------------------------

dkexpression *dkfunction_loc( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	DKFILE *filehandle;
	

	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	if( dkgetnextexpression( line ) )
	{
		dkexpfree(e);
		return NULL;					// error: syntax error - there should only be one parameter
	}
	
	dkexptoint( e );

	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}

	filehandle = dkfilebynum( ((int16_t *)e->data)[0] );
	if( !filehandle )
	{
		if( !dkstatus )
			dkstatus = DKSTATUS_BADFILENUMBER;		// TODO: find why Syntax error happens instead of bad file number error?
//		return NULL;								// because of return NULL?
		return e;
	}
	
	((int16_t *)e->data)[0] = dkftell( filehandle );


	return e;
}


//--------------------------------------------------------------------
dkexpression *dkfunction_string( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	uint16_t length=0;
	char repeatchar;
	int i;
	

	if( !(nameflags & DKNAME_STRING) )
	{
		dkstatus = DKSTATUS_TYPEMISMATCH;
		return NULL;
	}


	e = dkgetfirstexpression( line );	// e = first expression
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand
	
	dkexptoint( e );
	length = ((int16_t *)e->data)[0];
	if( e->type != DKVAR_INTEGER )
	{
		dkexpfree(e);
		return NULL;					// error: type mismatch
	}


	e = dkgetnextexpression( line );	// get second expression (character to repeat)
	line = expstr;
	if( !e )
		return NULL;					// error: missing operand

	if( e->type == DKVAR_STRING )
	{
		repeatchar = ((char **)e->data)[0][0];
	}
	else
	{
		dkexptoint( e );				// read ascii code for value to repeat
		if( e->type != DKVAR_INTEGER )
		{
			dkexpfree(e);
			return NULL;					// error: type mismatch
		}		
		repeatchar = ((uint16_t *)e->data)[0];
	}	
	dkexpfree(e);						
	
	if( dkgetnextexpression( line ) )
		dkexpfree(e);								// error: extra parameters
		
		
	e = dknewexp();						// create new expression to return
	if( !e )
		return NULL;

	e->type = DKVAR_STRING;
	e->data = (char **)malloc(sizeof(char *));
	((char **)e->data)[0] = (char *)malloc( length+1 );

	for( i=0; i<length; i++ )
		((char **)e->data)[0][i] = repeatchar;
		
	((char **)e->data)[0][length] = '\0';


	return e;
}


//--------------------------------------------------------------------

dkexpression *dkfunction_sysinfo( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	dkexpression *param;				// param will point to the result of expression passed to us
	char t[ MAX_TOKEN_LENGTH+1 ];


	if( !nameflags&DKNAME_PAREN )		// check that we found opening parenthesis (
		return NULL;					// error: syntax error
	if( !nameflags&DKNAME_STRING )		// check that we were called as sysinfo$
		return NULL;					// error: syntax error

	line = dkgettoken( line, t );		// get token - (

	param = expression( line );			// process expression @line
	if( !param )	
		return NULL;					// error: missing operand

	line = expstr;						// update line pointer to point to where expression() left off
	

	expstr = dkgettoken( line, t );		// get token - )
	if( t[0] != ')' )
	{
		dkexpfree(param);
		return NULL;					// error: syntax error (missing closing parenthesis)
	}

	if( param->type != DKVAR_STRING )
	{
		dkexpfree(param);
		return NULL;					// error: type mismatch
	}
	if( !strlen( ((char **)param->data)[0] ) )
	{
		dkexpfree(param);
		return NULL;					// error: illegal function call
	}


	e = dknewexp();
	if( !e )
	{
		dkexpfree(param);
		return NULL;					// memory allocation error
	}


	e->type = DKVAR_STRING;
	e->data = (char **)malloc( sizeof( char * ) );
	if( !e->data )
	{
		dkexpfree(e);
		dkexpfree(param);
		dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}
	
	strlwr( ((char **)param->data)[0] );
	
	if( !strcmp_P( ((char **)param->data)[0], PSTR("chipid") ) )
	{									// "chipid" = return 3-byte chipid of current device
		sprintf_P( t, PSTR("%02X%02X%02X"), boot_signature_byte_get(0), boot_signature_byte_get(2), boot_signature_byte_get(4) );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("fuses") ) )
	{		
		sprintf_P( t, PSTR("%02X%02X %02X %02X"), boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS), boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS), boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS), boot_lock_fuse_bits_get(GET_LOCK_BITS) );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("buildchipid") ) )
	{									// "buildchipid" = return 3-byte chipid this binary was built for
		sprintf_P( t, PSTR("%02X%02X%02X"), SIGNATURE_0, SIGNATURE_1, SIGNATURE_2 );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("ramend") ) )
	{								
		sprintf_P( t, PSTR("%0lX"), (long)RAMEND );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("xramend") ) )
	{								
		sprintf_P( t, PSTR("%0lX"), (long)XRAMEND );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("e2end") ) )
	{								
		sprintf_P( t, PSTR("%0lX"), (long)E2END );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("flashend") ) )
	{								
		sprintf_P( t, PSTR("%0lX"), (long)FLASHEND );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("f_cpu") ) )
	{								
		sprintf_P( t, PSTR("%g MHz"), (double)F_CPU/1000000 );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("version") ) )
	{								
		sprintf_P( t, PSTR("%3.2f"), (double)DKBASIC_VERNUM/100.0 );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("max_line_length") ) )
	{								
		sprintf_P( t, PSTR("%d"), (int)MAX_LINE_LENGTH );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else if( !strcmp_P( ((char **)param->data)[0], PSTR("max_token_length") ) )
	{								
		sprintf_P( t, PSTR("%d"), (int)MAX_TOKEN_LENGTH );
		((char **)e->data)[0] = (char *)malloc( strlen(t) );
		strcpy( ((char **)e->data)[0], t );
	}
	else		// unknown parameter passed, return empty string
	{
		((char **)e->data)[0] = (char *)malloc( 1 );
		((char **)e->data)[0][0] = '\0';
	}


	dkexpfree(param);


	return e;
}

//--------------------------------------------------------------------

dkexpression *dkfunction_len( char *line, uint16_t nameflags )
{
	dkexpression *e;					// e will point to the expression we return
	char t[ MAX_TOKEN_LENGTH+1 ];
    uint16_t varnameflags;    
    struct _dkvariable *var;
    char name[MAX_TOKEN_LENGTH+1];
    int dimsize;
    int i;
        
    
	if( !nameflags&DKNAME_PAREN )		// check that we found opening parenthesis (
		return NULL;					// error: syntax error

	line = dkgettoken( line, t );		// get token - (

    line = dkgettoken( line, t );       // get token - variable name
    strncpy( name, t, MAX_TOKEN_LENGTH );
    
   	line = dkgetnameflags( line, &varnameflags );

	expstr = dkgettoken( line, t );		// get token - )
	if( t[0] != ')' )
		return NULL;					// error: syntax error (missing closing parenthesis)


	e = dknewexp();
	if( !e )
		return NULL;					// memory allocation error

	e->type = DKVAR_INTEGER;
	e->data = (int16_t *)malloc( sizeof( int16_t ) );
	if( !e->data )
	{
		dkexpfree(e);
		dkstatus = DKSTATUS_OUTOFMEMORY;
		return NULL;
	}
		
	((int16_t *)e->data)[0] = 0;

    varnameflags |= DKVAR_ARRAY;

	var = dkgetvariable( name, varnameflags );			// get pointer to struct _dkvariable
    if( var )
    {
	    dimsize=1;
	    for( i=0; i<var->e.numdimensions; i++ )
	    {
		    dimsize *= (var->e.dimensions[i]+1);		// allocate an extra space in each dim for [0] element
	    }
        
	    ((int16_t *)e->data)[0] = dimsize;
    }



	return e;
}

//--------------------------------------------------------------------
